﻿using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using HtmlAgilityPack;
using RenderEngineInterfaces;

namespace SimpleHTMLRender
{
    /// <summary>
    /// A dirt simple HTML parser. Uses the title of the web page and long with its date of modification
    /// and looks for any links to pdf, ppt, etc. (anything we can render, except any other html).
    /// </summary>
    [Export(typeof(IRenderItem))]
    [RenderInfo(typeof(FileInfo))]
    public class HTMLRender : IRenderItem
    {
        /// <summary>
        /// Create a task to do the rendering we require
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public Task<Visual> Render(object what)
        {
            return Task<Visual>.Factory.StartNew(() => RenderHTML(what as FileInfo));
        }

        /// <summary>
        /// Engine to do the sub-rendering
        /// </summary>
        [Import]
#pragma warning disable 0649
        IRenderEngine MyRender;
#pragma warning restore 0649

        /// <summary>
        /// Utility for doing some layout stuff.
        /// </summary>
        [Import]
#pragma warning disable 0649
        ILayoutHelper MyLayoutHelper;
#pragma warning restore 0649

        /// <summary>
        /// If we should be using a different temp directory than the one that is there by default... :-)
        /// </summary>
        [Import("RenderingTempDir", AllowDefault = true)]
#pragma warning disable 0649
        private DirectoryInfo RenderingTempDir;
#pragma warning restore 0649

        /// <summary>
        /// Does the actual rendering work for this html file (and parsing, etc.)
        /// </summary>
        /// <param name="htmlFile"></param>
        /// <returns></returns>
        private Visual RenderHTML(FileInfo htmlFile)
        {
            HtmlDocument doc = new HtmlDocument();
            using (var reader = htmlFile.OpenText())
            {
                doc.LoadHtml(reader.ReadToEnd());
                reader.Close();
            }

            ///
            /// Extract a title
            /// 

            string title = doc.DocumentNode.ExtractHTMLTitle();

            ///
            /// Get the last modified date. Hopefully this is "correct"
            /// 

            DateTime lastModified = htmlFile.LastWriteTime;

            ///
            /// Look at all links (a), with a href.
            /// 

            var links = doc.DocumentNode.SelectNodes("//a[@href]");

            var allGoodLinks = from l in new EnumerableWrapper<HtmlNode>(links)
                               let href = l.Attributes["href"].Value
                               where href.StartsWith("http")
                               let u = new Uri(href)
                               let ext = Path.GetExtension(u.AbsolutePath)
                               where ext != null && ext.Length > 0 && !ValidExtensions.Contains(ext)
                               select new
                               {
                                   Link = u,
                                   Title = l.InnerText.CleanHTMLText()
                               };

            ///
            /// Next, do the rendering, and then wait for everyone.
            /// 

            var allTaskInfo = (from l in allGoodLinks
                               select new
                               {
                                   T = MyRender.RenderToFuture(l.Link, forceAsComposableUnit: true),
                                   Title = l.Title
                               }).ToArray();
            Task<Visual>.WaitAll(allTaskInfo.Select(l => l.T).ToArray());

            ///
            /// We want a very simple title slide around each guy. Normalize
            /// the size before we do that.
            /// 

            var talks = allTaskInfo.Where(t => t.T.Result != null).Select(t =>
                {
                    var v = t.T.Result;
                    v.ScaleToWidth(11.0);
                    VisualTitleAndBackground vt = new VisualTitleAndBackground(v, RenderingTempDir);
                    vt.Add(1.0, t.Title);
                    vt.Info = new Visual.VisualInfo() { Title = t.Title };
                    return vt;
                });

            ///
            /// Do the layout now
            /// 

            var allTalks = MyLayoutHelper.LayoutIn2By3Rows(talks.ToArray());

            ///
            /// Now, put together the master holder for the whole thing.
            /// 

            allTalks.ScaleToWidth(10.0);

            ///
            /// Next, do the title
            /// 

            VisualTitleAndBackground tit = new VisualTitleAndBackground(allTalks, RenderingTempDir);
            tit.Add(1.0, title);

            tit.Info = new Visual.VisualInfo() { Title = lastModified.ToString() + " - " + title, Info = new string[] { lastModified.ToString() } };

            return tit;
        }

        /// <summary>
        /// Valid strings that we can deal with.
        /// </summary>
        private static string[] ValidExtensions = new string[] { ".html", ".htm", ".shtml" };

        /// <summary>
        /// We are interested in rendering only html. Do that by the tupe of file, not
        /// by the response of the web browser. Yeah, I know.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public RenderPluginPriority RenderPriority(object what)
        {
            if (what == null)
            {
                return RenderPluginPriority.RPPCantDoIt;
            }
            FileInfo u = what as FileInfo;
            if (u == null)
            {
                return RenderPluginPriority.RPPCantDoIt;
            }

            if (ValidExtensions.Contains(u.Extension))
            {
                return RenderPluginPriority.RPPGeneric;
            }
            return RenderPluginPriority.RPPCantDoIt;

        }
    }
}
